#pragma once

#include <functional>
#include "Socket.hpp"
#include "Log.hpp"
#include "Epoll.hpp"


namespace ns_epoll_server
{
    using func_t = std::function<void(const std::string&)>;

    static const int gl_epollsize = 512;
    static const int gl_eventssize = 52;

    class EpollServer
    {
    public:
        EpollServer(func_t handler, const uint16_t& port = 8080)
            :_epoll_size(gl_epollsize), _events_size(gl_eventssize), _handler(handler)
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, port);
            Sock::Listen(_listensock);

            // 创建epoll
            int epfd = Epoll::Epoll_create(_epoll_size);
            if(epfd < 0) exit(11);
            _epfd = epfd;

            // epoll添加listensock
            if(!Epoll::Epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, EPOLLIN)) exit(12);

            _events = new struct epoll_event[_events_size];
            if(_events == nullptr) exit(13);
        }
        
        void Start()
        {
            while(true)
            {
                // timeout 1000
                LoopOnce(1000);
            }
        }

        ~EpollServer()
        {
            if(_listensock >= 0) close(_listensock);
            if(_epfd >= 0) close(_epfd);
            if(_events) delete[] _events;
        }
    private:
        void LoopOnce(int timeout)
        {
            int size = Epoll::Epoll_wait(_epfd, _events, _events_size, timeout);
            if(size < 0) exit(14);
            else if(size == 0) return;
            // size > 0
            HandlerEvents(size);
        }

        void HandlerEvents(int size)
        {
            for(int i = 0; i < size; ++i)
            {
                // _events[i].data.fd : fd
                // _events[i].events : EPOLLIN
                if(!(_events[i].events & EPOLLIN)) continue;
                if(_events[i].data.fd == _listensock) Acceper();
                else Recver(i);
            }
        }

        void Acceper()
        {
            int sock = Sock::Accept(_listensock, nullptr, nullptr);
            Epoll::Epoll_ctl(_epfd, EPOLL_CTL_ADD, sock, EPOLLIN);
        }

        void Recver(int pos)
        {
            char buffer[1024];
            int sock = _events[pos].data.fd;
            ssize_t sz = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if(sz < 0)
            {
                Messagelog(ERROR, "recv sock[%d] error %d:%s", sock, errno, strerror(errno));
            }
            else if(sz == 0)
            {
                Messagelog(NORMAL, "client %d quit, me too ...", sock);
                Epoll::Epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, 0);
                close(sock);
            }
            else
            {
                buffer[sz - 1] = 0;
                _handler(buffer);
            }
        }

    private:
        int _listensock;
        int _epfd;
        struct epoll_event* _events;

        int _epoll_size;
        int _events_size;
        func_t _handler;
    };
} 
